/*
 main.cpp
 
 Revision Date: May 9, 2011
 GSP 420: Team E
 
 This file constructs the window and basic keyboard commands
 to help us build the main core of our game engine.
 
 Press 'c' to disable or enable backface culling. 
 */

//=============================================================
//Make sure to comment out the correct macro in order to ensure
//the project builds correclty for your OS!!! (If you received
//code from Scott and you are on Windows comment out Mac and 
//uncomment Windows. The file HelperFunctions.h also has a 
//macro that needs commented out as well.
//=============================================================

//#define OS_MAC
#define OS_WINDOWS 

//MAC OSX INCLUDES
#if defined (OS_MAC)
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#endif 

//WINDOWS INCLUDES
#if defined (OS_WINDOWS)
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/freeglut.h>
#endif
 
#include <iostream>
#include "OGLApp.h"
#include "BaseEntity.h"
#include "GraphicsObject.h"
#include "CustomDataTypes.h"
#include "HelperFunctions.h"

using namespace std;

//Arrays for key presses
bool* keyStates = new bool[256]; //Regular keys
bool* keySpecialStates = new bool[256]; //Special keys

//Globals for menu options
bool cullingEnabled = true;

//Forwared declarations
void timer(int iUnused);
void keyDown(unsigned char key, int x, int y);
void keyUp(unsigned char key, int x, int y);
void keySpecialDown(int key, int x, int y);
void keySpecialUp(int key, int x, int y);
void keyOperations(void);
void keySpecialOperations(void);
void display(void);
void reshape(int width, int height);

//Create singleton
OGLApp* newApp = OGLApp::Instance(); //Create singleton instance


//======================================================================
//                              Main Function
//======================================================================
int main(int argc, char** argv)
{
    //Initialize key state arrays
    std::fill_n( keyStates, 256, false);
    std::fill_n( keySpecialStates, 256, false);
    
    //Initialize GLUT
    glutInit(&argc, argv);
    
    newApp->onInit(); //Initialize all subsystems
    
    //Initialize functions with callbacks that can't be abstracted
    //Window Control Functions
    glutDisplayFunc(display); //Tell glut to draw window based on function
    glutReshapeFunc(reshape); //Tell glut to reshape the window when dragged
    
    //Keyboard Control Functions
    glutKeyboardFunc(keyDown); //Tell glut to use keyDown function to handle key presses
    glutKeyboardUpFunc(keyUp); //Tell glut to use keyUp function to handle key releases
    glutSpecialFunc(keySpecialDown); //Tell glut to use keySpecialDown function to handle special key events
    glutSpecialUpFunc(keySpecialUp); //Tell glut to use keySpecialUp function to handle special key up events
    
    //This will be the main game loop but will not be put into a loop
    //until the UI core starts handling input
    newApp->onEvent();
    
    //Update the game objects for render (cannot be completed until SDL is implemented.
    //newApp->onUpdate();
    
    
    //=============================================
    //Temporary test objects to show our core works
    //=============================================
    
    //Creating a new object derived from the BaseEntity class
    //This is a graphics object, but there can also be Physics, UI, or AI objects
    
    GraphicsObject *gObj1 = new GraphicsObject(1); //Default constructor with just an ID
    
    gObj1->setObjectPosition(-2.0, 2.0, -5.0); //The render function will read the position of the game objects.
    gObj1->setObjectType(CUBE); //This method sets the object type of the object.
    gObj1->setObjectRotation(1.0, 0.0, 1.0);
    
    cout << endl << endl   << "----gObj1-----" << endl;
    cout << "Object ID:  "  << gObj1->ID() << endl;
    cout << "X Position: " << gObj1->getObjectPositionX() << endl; //Return the X, Y, and Z positions of the object
    cout << "Y Position: " << gObj1->getObjectPositionY() << endl;
    cout << "Z Position: " << gObj1->getObjectPositionZ() << endl;
    
    //Create a second graphics object:
    GraphicsObject *gObj2 = new GraphicsObject(2, 0.0, 0.0, -5.0); //Second constructor was used, it takes an ID and position
    gObj2->setObjectType(CUBE);
    
    cout << endl << endl   << "----gObj2-----" << endl;
    cout << "Object ID:  "  << gObj2->ID() << endl;
    cout << "X Position: " << gObj2->getObjectPositionX() << endl; //Return the X, Y, and Z positions of the object
    cout << "Y Position: " << gObj2->getObjectPositionY() << endl;
    cout << "Z Position: " << gObj2->getObjectPositionZ() << endl;
    
    //Create a third object with the final constructor
    GraphicsObject *gObj3 = new GraphicsObject(3, CUBE, 2.0, -2.0, -5.0);
    gObj3->setObjectRotation(0.0, 0.0, 1.0);
    cout << endl << endl   << "----gObj3-----" << endl;
    cout << "Object ID:  "  << gObj3->ID() << endl;
    cout << "X Position: " << gObj3->getObjectPositionX() << endl; //Return the X, Y, and Z positions of the object
    cout << "Y Position: " << gObj3->getObjectPositionY() << endl;
    cout << "Z Position: " << gObj3->getObjectPositionZ() << endl;
    
    //Create a fourth object by filling in all known parameters
    GraphicsObject *gObj4 = new GraphicsObject(4, PYRAMID, -3.0, -2.0, -10.0, 1.0, 0.0, 0.0, 0.5, 0.5, 0.5);
    
    //Add objects to the mObjectList
    newApp->addElement(*gObj1);
    newApp->addElement(*gObj2);
    newApp->addElement(*gObj3);
    newApp->addElement(*gObj4);
    
    //Remove objects from the mObjectList
    newApp->removeElement(*gObj3);
    
    //TEST UPDATE
    newApp->onUpdate();
    
    
    //Timer to control framerate
	timer(0);
    
    //Glutloop to keep program running
    glutMainLoop();
    
    //This will be called after the program exits the game loop
    newApp->onExit();
	
	return 0;
}



//======================================================================
//                  Functions to manage windows and input
//======================================================================


//Timer function
void timer(int iUnused)
{
    glutPostRedisplay(); //Refresh the screen
    glutTimerFunc(30, timer, 0); //Keeps frame rate at 30 FPS despite hardware capabilites 
}

//Function to handle character key down events
void keyDown(unsigned char key, int x, int y)
{
	//Set key to 'pressed'
    keyStates[key] = true;
    
	//Check for key operations each frame to avoid multiple keypresses per frame
    keyOperations();
	int ignore = 1; //flag to ignore multiple keypresses
	glutIgnoreKeyRepeat(ignore);
}

//Function to handle key up events
void keyUp(unsigned char key, int x, int y)
{
    //Set key to 'not pressed'
    keyStates[key] = false;
}

//Function to handle special key down events
void keySpecialDown(int key, int x, int y)
{
    //Set key to 'pressed'
    keySpecialStates[key] = 1;
    
	//Check for key operations each frame to avoid multiple keypresses per frame
    keySpecialOperations();
	int ignore = 1; //flag to ignore multiple keypresses
	glutIgnoreKeyRepeat(ignore);
}

//Function to handle special key up events
void keySpecialUp(int key, int x, int y)
{
    //Set key to 'not pressed'
    keySpecialStates[key] = 0;
}

//Function to control what happens on key state changes for regular keys
void keyOperations(void)
{
    //These can obviously be changed and added to to provide UI functionality
    if(keyStates['a']) {printf("A is pressed.\n");}
    
    if(keyStates['b']) {printf("B is pressed.\n");}
    
	if(keyStates['c'])
	{
		if(!cullingEnabled){
			printf("CULLING IS ENABLED\n");
			glEnable(GL_CULL_FACE);
			cullingEnabled = true;}
        
		else{
			printf("CULLING IS DISABLED\n");
			glDisable(GL_CULL_FACE);
			cullingEnabled = false;}
	}
    
	//...etc
}

//Function to control what happens on special key state changes
void keySpecialOperations(void)
{
    //These can obviously be changed and added to to provide UI functionality
    if(keySpecialStates[GLUT_KEY_LEFT]) {printf("The Left Key was pressed.\n");}
    
    if(keySpecialStates[GLUT_KEY_RIGHT]) {printf("The Right Key was pressed.\n");}
    
    if(keySpecialStates[GLUT_KEY_UP]) {printf("The Up Key was pressed.\n");}
    
    if(keySpecialStates[GLUT_KEY_DOWN]) {printf("The Down Key was pressed.\n");}
}

//Function to draw the scene
void display()
{
    newApp->onRender();
}

//Function to reshape the window when dragged
void reshape(int width, int height)
{
    //Set viewport to window size
    glViewport(0, 0, (GLsizei)width, (GLsizei)height); 
    
    // Switch to projection matrix to manipulate how scene is viewed
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); //Reset projection matrix to avoid artifacts
    
    //Set FOV, + Near and Far clip planes
    gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0);
    
    //Switch back to modelview matrix
    glMatrixMode(GL_MODELVIEW);
}


//----------------------------------------------------------------------

